using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Xml;
using System.IO;
using System.Xml.Schema;

/// <summary>
/// Summary description for TEILibrary
/// </summary>
namespace TEILib
{
    public class TEILibrary : IEnumerable
    {
        private ArrayList arrLibrary = new ArrayList();

        #region Constructor
        public TEILibrary()
        {
        }

        public TEILibrary(string LibPath)
        {
            ArrayList arrLib = getFiles(LibPath);
            for (int i = 0; i < arrLib.Count; i++)
            {
                TEIDocument TEIDoc = new TEIDocument(LibPath + "\\" + arrLib[i].ToString());
                arrLibrary.Add(TEIDoc);
            }

        }
        #endregion

        #region Properties
        private string _status;
        public string Status
        {
            get { return _status; }
            set { _status = value; }
        }
        #endregion
        //private enumurator class
        private class TEILibEnumerator : IEnumerator
        {
            public ArrayList arrLibrary = new ArrayList();
            int position = -1;

            //constructor
            public TEILibEnumerator(ArrayList array)
            {
                arrLibrary = array;
            }

            //IEnumerator and IEnumerable require these methods.
            private IEnumerator GetEnumerator()
            {
                return (IEnumerator)this;
            }

            //IEnumerator
            public bool MoveNext()
            {
                position++;
                return (position < arrLibrary.Count);
            }

            //IEnumerable
            public void Reset()
            { position = -1; }

            //IEnumerable
            public object Current
            {
                get
                {
                    try
                    {
                        return arrLibrary[position];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        throw new InvalidOperationException();
                    }
                }
            }
        }//end nested class
        public IEnumerator GetEnumerator()
        {
            return new TEILibEnumerator(arrLibrary);
        }

        /// <summary>
        /// Given a directory get a list of files it contains.
        /// </summary>
        /// <param name="strFolderPath">The directory path to get the files in.</param>
        /// <returns>An array of files.</returns>
        public ArrayList getFiles(String strFolderPath)
        {
            //create a directoryinfo object
            DirectoryInfo objDirInfo = new DirectoryInfo(strFolderPath);

            //get the FileInfo array
            FileInfo[] arrFileInfo = objDirInfo.GetFiles();

            ArrayList arrFiles = new ArrayList();

            for (int i = 0; i < arrFileInfo.Length; i++)
            {
                arrFiles.Add(arrFileInfo[i].Name);
                //arrFiles[i] = arrFileInfo[i].Name;
            }

            return arrFiles;
        }
        /// <summary>
        /// Validate the xml documents in the library.
        /// </summary>
        /// <param name="xsdPath">The xsd file used to validate the xml.</param>
        /// <param name="targetNamespace">The target namespace of the schema.</param>
        public void Validate(string xsdPath, string targetNamespace)
        {

            ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
            foreach (TEIDocument objTEIDoc in this)
            {
                try
                {
                    //add the schema
                    //objTEIDoc.Schemas.Add("http://www.tei-c.org/ns/1.0", xsdPath);
                    objTEIDoc.Schemas.Add(targetNamespace, xsdPath);

                    //write the document name
                    this.Status = "Validating " + objTEIDoc.FileName;
                    //Console.WriteLine(objTEIDoc.FileName);

                    //now validate
                    objTEIDoc.Validate(eventHandler);
                }
                catch (Exception ex)
                {
                    throw new TEILibException("TEILibrary.Validate(string, string)", ex);
                    //Console.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// Handle validation messages.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ValidationEventHandler(object sender, ValidationEventArgs e)
        {
            switch (e.Severity)
            {
                case XmlSeverityType.Error:
                    this.Status = "Error: " + e.Message;
                    //Console.WriteLine("Error: {0}", e.Message);
                    break;
                case XmlSeverityType.Warning:
                    this.Status = "Warning: " + e.Message;
                    //Console.WriteLine("Warning {0}", e.Message);
                    break;
            }
        }
        ///<summary>
        ///Creates a bibliography entry for each document in the library.
        ///<param name="BibPath">The path to save the bibliography file.</param>
        ///<param name="author">The author of the bibliography.</param>
        ///</summary>
        public TEIDocument createBibliography(string BibPath, ArrayList author)
        {
            TEIDocument objBibliography = new TEIDocument();

            //create and add the declaration
            XmlNode objDeclaration = objBibliography.CreateNode(XmlNodeType.XmlDeclaration, "", "");
            objBibliography.AppendChild(objDeclaration);

            //create and add the root element
            XmlNode objRoot = objBibliography.CreateElement("TEI");
            XmlAttribute objAttrib = objBibliography.CreateAttribute("xmlns");
            objAttrib.Value = "http://www.tei-c.org/ns/1.0";
            objRoot.Attributes.Append(objAttrib);
            objBibliography.AppendChild(objRoot);

            //create and append teiHeader
            objBibliography.author = author;
            objBibliography.title = "Bibliography";
            XmlElement objTeiHeader = objBibliography.getTeiHeader(objBibliography.author, objBibliography.title);
            objRoot.AppendChild(objTeiHeader);

            //create and add text, body, div elements
            //create a text element
            XmlElement objText = objBibliography.CreateElement("text");
            objRoot.AppendChild(objText);

            //create a body
            XmlElement objBody = objBibliography.CreateElement("body");
            objText.AppendChild(objBody);

            //create a div
            XmlElement objDiv = objBibliography.CreateElement("div");
            objBody.AppendChild(objDiv);

            //append a p to the div
            XmlElement objP = objBibliography.CreateElement("p");
            objDiv.AppendChild(objP);

            //append the listBibl to a div
            XmlElement objListBibl = objBibliography.CreateElement("listBibl");
            objDiv.AppendChild(objListBibl);

            foreach (TEIDocument objTEIDoc in this)
            {
                //get the bibliographic entry from the document
                XmlNode objBibEntry = objTEIDoc.getBibEntry();
                objListBibl.AppendChild(objBibliography.ImportNode(objBibEntry, true));
            }

            //save the xml
            objBibliography.Save(BibPath + "\\bibliography.xml");

            return objBibliography;
        }

        /// <summary>
        /// Create a list of footnotes, endnotes, etc. This is useful for finding referenced documents.
        /// </summary>
        /// <param name="FilePath">The file path of the resulting document.</param>
        /// <param name="author">The author of the resulting document.</param>
        /// <returns></returns>
        public TEIDocument CreateListOfNotes(string FilePath, ArrayList author)
        {
            TEIDocument objTEIDoc = new TEIDocument();

            //create and add the declaration
            XmlNode objDeclaration = objTEIDoc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
            objTEIDoc.AppendChild(objDeclaration);

            //create and add the root element
            XmlNode objRoot = objTEIDoc.CreateElement("TEI");
            XmlAttribute objAttrib = objTEIDoc.CreateAttribute("xmlns");
            objAttrib.Value = "http://www.tei-c.org/ns/1.0";
            objRoot.Attributes.Append(objAttrib);
            objTEIDoc.AppendChild(objRoot);

            //create and append teiHeader
            objTEIDoc.author = author;
            objTEIDoc.title = "List of Notes";
            XmlElement objTeiHeader = objTEIDoc.getTeiHeader(objTEIDoc.author, objTEIDoc.title);
            objRoot.AppendChild(objTeiHeader);

            //create and add text, body, div elements
            //create a text element
            XmlElement objText = objTEIDoc.CreateElement("text");
            objRoot.AppendChild(objText);

            //create a body
            XmlElement objBody = objTEIDoc.CreateElement("body");
            objText.AppendChild(objBody);

            foreach (TEIDocument objTEIDoc2 in this)
            {
                //get the list of notes from the document
                XmlNodeList objNotes = objTEIDoc2.SelectNodes("//tei:note", objTEIDoc.NamespaceManager);

                if (objNotes != null)
                {
                    //create a div
                    XmlElement objDiv = objTEIDoc.CreateElement("div");
                    objBody.AppendChild(objDiv);

                    //create a head
                    XmlElement objHead = objTEIDoc.CreateElement("head");
                    objDiv.AppendChild(objHead);
                    objHead.InnerText = objTEIDoc2.title;

                    //create a list
                    XmlElement objList = objTEIDoc.CreateElement("list");
                    objDiv.AppendChild(objList);
                    foreach (XmlNode note in objNotes)
                    {
                        XmlElement objItem = objTEIDoc.CreateElement("item");
                        foreach (XmlNode xn in note.ChildNodes)
                        {
                            //create a new element
                            XmlElement objElement = objTEIDoc.CreateElement(xn.Name);
                            objElement.InnerXml = xn.InnerXml;
                            objItem.AppendChild(objTEIDoc.ImportNode(xn, true));
                        }
                        objList.AppendChild(objItem);
                    }
                }
            }
            //save the xml
            objTEIDoc.Save(FilePath);

            return objTEIDoc;
        }
        /// <summary>
        /// Transform the documents in the library.
        /// </summary>
        /// <param name="XslPath">The path to the XSL used in the transform.</param>
        /// <param name="result">The directory path used to store the resulting files.</param>
        public void Transform(string XslPath, string result)
        {
            foreach (TEIDocument objTEIDoc in this)
            {
                objTEIDoc.Transform(XslPath, result);
                this.Status = "Transforming " + objTEIDoc.FileName;
            }
        }

        /// <summary>
        /// Get a simple word frequency for a library.
        /// </summary>
        /// <returns>A Hashtable containing words as keys and the frequency as values.</returns>
        public Hashtable WordFrequency()
        {
            Hashtable docHash;
            Hashtable libHash = new Hashtable();

            
            foreach (TEIDocument objTEIDoc in this)
            {
                Console.WriteLine("Getting word frequency for {0}", objTEIDoc.FileName);

                docHash = objTEIDoc.WordFrequency();

                foreach (DictionaryEntry word in docHash)
                {
                    if (libHash.ContainsKey(word.Key))
                    {
                        libHash[word.Key] = (int)libHash[word.Key] + (int)word.Value;
                    }
                    else
                    {
                        libHash.Add(word.Key, word.Value);
                    }
                }
                docHash.Clear();
            }

            return libHash;
        }

    }
}
